/*
 * Copyright (C) 2008-2011 CBDP Consortium
 * 
 * All rights reserved.
 *
 * This software is licensed as described in the file COPYING, which
 * you should have received as part of this distribution.
 * 
 * This software consists of contributions made by many individuals, 
 * listed below:
 *
 * Author: 	Xabier Laiseca <xabier.laiseca@deusto.es>
 * 			David Ausín <david.ausin@deusto.es>
 */
package cbdp.server.contextreasoner.configuration;

import java.io.IOException;
import java.io.InputStream;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import cbdp.server.contextreasoner.configuration.exceptions.OntologyConfigurationLoaderException;
import cbdp.server.contextreasoner.configuration.model.Configuration;

public class OntologyConfigurationLoader
{
	private final static String DEFAULT_CONFIGURATION_SCHEMA_FILE_LOCATION = "/resources/configuration/configuration.xsd";
	
	private final static String ONTOLOGY_TAG = "ontology";
	private final static String MAPPING_FILE_TAG = "mapping-file";
	private final static String STORAGE_CONFIGURATION_FILE_TAG = "storage-configuration";
	
	private final static String TYPE_ATTRIBUTE = "type";
	private final static String LOCATION_ATTRIBUTE = "location";
	private final static String NAME_ATTRIBUTE = "name";
	
	private final static String REASONER_TAG = "reasoner";
	private final static String REASONER_CLASS_NAME_ATTRIBUTE = "class";

	private static final String ONTOLOGY_SPEC_TAG = "ontology-spec";
	private static final String ONTOLOGY_SPEC_CLASS_ATTRIBUTE = "class";
	private static final String ONTOLOGY_SPEC_ATTRIBUTE_ATTRIBUTE = "attribute";
	
	private final static String CAN_NOT_LOAD_CONFIGURATION = "The configuration can not be loaded";


	public static Configuration load(final String location) throws OntologyConfigurationLoaderException
	{
		final Configuration configuration = new Configuration();

		if (isValidXML(location, DEFAULT_CONFIGURATION_SCHEMA_FILE_LOCATION))
		{
			final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
	
			try
			{
				final DocumentBuilder builder = factory.newDocumentBuilder();
				final Document document = builder.parse(OntologyConfigurationLoader.class.getResourceAsStream(location));
	
				loadOntologyConfiguration(document, configuration);
				loadMappingFileConfiguration(document, configuration);
				loadStorageConfiguration(document, configuration);
				loadReasonerConfiguration(document, configuration);
				loadOntologySpec(document, configuration);
			} 
			catch (final Exception e)
			{
				throw new OntologyConfigurationLoaderException(CAN_NOT_LOAD_CONFIGURATION, e);
			}
		}
		else throw new OntologyConfigurationLoaderException(CAN_NOT_LOAD_CONFIGURATION);
		
		return configuration;
	}
	
	private static void loadOntologyConfiguration(final Document document, final Configuration configuration)
	{
		final NodeList ontologyNodes = document.getDocumentElement().getElementsByTagName(ONTOLOGY_TAG);
		
		for (int i = 0; i < ontologyNodes.getLength(); i++)
		{
			final Element ontologyElement = (Element)ontologyNodes.item(i);
			
			final String name = ontologyElement.getAttribute(NAME_ATTRIBUTE);
			final String location = ontologyElement.getAttribute(LOCATION_ATTRIBUTE);
			
			configuration.addOntologyLocation(name, location);
		}
	}
	
	private static void loadMappingFileConfiguration(final Document document, final Configuration configuration)
	{
		final Element mappingFileElement = (Element)document.getDocumentElement().getElementsByTagName(MAPPING_FILE_TAG).item(0);
		
		if (mappingFileElement != null)
		{
			final String location = mappingFileElement.getAttribute(LOCATION_ATTRIBUTE);
			
			configuration.setMappingFileLocation(location);
		}
	}
	
	private static void loadStorageConfiguration(final Document document, final Configuration configuration)
	{
		final Element storageElement = (Element)document.getDocumentElement().getElementsByTagName(STORAGE_CONFIGURATION_FILE_TAG).item(0);
		
		if (storageElement != null)
		{
			final String type = storageElement.getAttribute(TYPE_ATTRIBUTE);
			final String location = storageElement.getAttribute(LOCATION_ATTRIBUTE);
			
			configuration.setStorageType(type);
			configuration.setStorageConfigurationFileLocation(location);
		}
	}
	
	private static void loadReasonerConfiguration(final Document document, final Configuration configuration)
	{
		final Element reasonerElement = (Element)document.getDocumentElement().getElementsByTagName(REASONER_TAG).item(0);
		
		final String className = reasonerElement.getAttribute(REASONER_CLASS_NAME_ATTRIBUTE);
		configuration.setReasonerClassName(className);
	}
	
	private static void loadOntologySpec(final Document document, final Configuration configuration)
	{
		final Element ontologySpecElement = (Element)document.getDocumentElement().getElementsByTagName(ONTOLOGY_SPEC_TAG).item(0);
		
		if(ontologySpecElement != null)
		{
			final String clazz = ontologySpecElement.getAttribute(ONTOLOGY_SPEC_CLASS_ATTRIBUTE);
			final String attribute = ontologySpecElement.getAttribute(ONTOLOGY_SPEC_ATTRIBUTE_ATTRIBUTE);
			
			configuration.setOntologySpecClassName(clazz);
			configuration.setOntologySpecAttributeName(attribute);
		}
	}
	
	private static boolean isValidXML(final String xmlLocation, final String schemaLocation)
	{
        try 
        {
        	 // 1. Lookup a factory for the W3C XML Schema language
            final SchemaFactory factory = SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema");
            
            // 2. Compile the schema. 
            // Here the schema is loaded from a java.io.File, but you could use 
            // a java.net.URL or a javax.xml.transform.Source instead.
            
            final Schema schema = factory.newSchema(OntologyConfigurationLoader.class.getResource(schemaLocation));
        
            // 3. Get a validator from the schema.
            final Validator validator = schema.newValidator();
            
            // 4. Parse the document you want to check.
            final InputStream configurationXml = OntologyConfigurationLoader.class.getResourceAsStream(xmlLocation); 
            final Source source = new StreamSource(configurationXml);
            
            // 5. Check the document
            validator.validate(source);
            return true;
        }
        catch (SAXException ex) 
        {
        	return false;
        }
        catch (IOException e)
        {
			return false;
		}  
	}
}
